package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.CourseTeacherDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.constant.CoursePubTemplateKey;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.predicate.CourseAuditStatusPredicate;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author 杨佳畅
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private CourseTeacherService courseTeacherService;

    @Autowired
    private CoursePubMsgService coursePubMsgService;

    @Qualifier("com.xuecheng.content.agent.SystemApiAgent")
    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${course.publish.exchange}")
    private String exchange;

    @Value("${course.publish.routing-key}")
    private String routingKey;


    /**
     * 预览课程
     * 1.生成 CoursePub 数据并保存
     * 2.根据 CoursePub 内容构建 DataMap
     *
     * @param courseBaseId 课程ID
     * @param companyId    机构ID
     * @return
     */
    @Override
    @Transactional
    public Map<String, Object> previewCourse(Long courseBaseId, Long companyId) {
        // 生成 CoursePub 数据
        CoursePub coursePub = generateCoursePub(courseBaseId, companyId, false);
        // 生成 DataMap 数据
        Map<String, Object> dataMap = generateDataMap(coursePub);

        return dataMap;
    }

    /**
     * 生成课程发布数据
     * 1.查询课程基础信息
     * 2.查询课程营销数据
     * 3.查询课程计划数据
     * 4.查询课程教师数据
     * 5.组装数据并保存
     *
     * @param courseBaseId 课程ID
     * @param companyId    机构ID
     * @param isPublish    true: 发布课程 / false: 预览课程
     * @return
     */
    private CoursePub generateCoursePub(Long courseBaseId, Long companyId, boolean isPublish) {
        if (ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        // 获取课程基础信息
        // 该方法已经判断了课程是否存在、课程删除状态、是否是同一家机构
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseBaseId, companyId);
        // 检查课程审核状态（只能是未提交、审核未通过)
        String auditStatus = courseBase.getAuditStatus();

        if (isPublish) {
            // 只能对审核通过的课程进行发布
            boolean isCreateStatus = CourseAuditStatusPredicate.predicate(auditStatus, code -> CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus));
            ExceptionCast.cast(!isCreateStatus, ContentErrorCode.E_120201);
        } else {
            // 只能对未提交、审核未通过的课程进行预览
            boolean isCreateStatus = CourseAuditStatusPredicate.predicate(auditStatus, code -> CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(auditStatus) ||
                    CourseAuditEnum.AUDIT_DISPAST_STATUS.getCode().equals(auditStatus));
            ExceptionCast.cast(!isCreateStatus, ContentErrorCode.E_120015);
        }

        // 查询课程营销数据
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseBase.getCourseBaseId());
        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);

        // 查询课程计划，树形结构
        TeachplanDTO teachplanTreeNodes = teachplanService.queryTreeNodes(courseBaseId, companyId);

        // 查询课程教师
        List<CourseTeacherDTO> courseTeacherList = courseTeacherService.queryCourseTeacherById(courseBaseId, companyId);

        // 获取大分类、小分类
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        // 远程调用系统服务，获取分类信息
        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getCourseCategoryById4s(mt);
        if (!mtResponse.isSuccessful()) {
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(), mtResponse.getMsg());
        }

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getCourseCategoryById4s(st);
        if (!stResponse.isSuccessful()) {
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(), stResponse.getMsg());
        }

        CourseCategoryDTO mtEntity = mtResponse.getResult();
        CourseCategoryDTO stEntity = stResponse.getResult();

        // 构建CoursePub数据
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);

        // 课程营销数据json
        String marketJsonString = JsonUtil.objectTojson(courseMarket);
        coursePub.setMarket(marketJsonString);
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        // 课程计划json
        String teachplanJsonString = JsonUtil.objectTojson(teachplanTreeNodes);
        coursePub.setTeachplan(teachplanJsonString);

        // 课程教师json
        String teacherJsonString = JsonUtil.objectTojson(courseTeacherList);
        coursePub.setTeachers(teacherJsonString);

        // 课程分类数据
        coursePub.setMtName(mtEntity.getName());
        coursePub.setStName(stEntity.getName());

        // 保存课程发布数据
        // 一个courseBase数据对应一个coursePub数据
        LambdaQueryWrapper<CoursePub> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(CoursePub::getCourseId, courseBaseId);
        queryWrapper.eq(CoursePub::getCompanyId, companyId);

        // TODO: 只需要courseId，自定义SQL
        CoursePub po = this.getOne(queryWrapper);
        CoursePubMapper baseMapper = this.getBaseMapper();
        Long pubCourseId = baseMapper.queryCourseId(courseBaseId, companyId);


        boolean result = false;
        if (ObjectUtils.isEmpty(pubCourseId)) {
            // 不存在则保存
            coursePub.setCourseId(courseBase.getCourseBaseId());
            // 设置发布状态为未发布
            coursePub.setIsPub(1);
            result = this.save(coursePub);
        } else {
            // 存在则修改课程发布数据
            coursePub.setId(po.getId());
            result = this.updateById(coursePub);
        }

        ExceptionCast.cast(!result, ContentErrorCode.E_120205);

        return coursePub;
    }

    /**
     * 生成dataMap
     *
     * @param coursePub
     * @return
     */
    private Map<String, Object> generateDataMap(CoursePub coursePub) {
        // 构建数据模型对象
        Map<String, Object> dataMap = Maps.newHashMap();

        // 构建CoursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 构建课程营销数据
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);
        dataMap.put(CoursePubTemplateKey.COURSEMARKET, courseMarket);

        //构建课程计划
        String teachplanJson = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJson, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE, teachplanDTO);

        // 构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS, values);

        // 构建课程教师数据
        String teachers = coursePub.getTeachers();
        List<CourseTeacherDTO> teacherList = JsonUtil.jsonToList(teachers, CourseTeacherDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHERKEY, teacherList);

        return dataMap;
    }

    /**
     * 课程发布
     *
     * @param courseId  课程ID
     * @param companyId 机构ID
     * @param isResend  是否重新发送消息 true: 重新发送/false: 不需要重新发送
     */
    @Override
    @Transactional
    public void publish(Long courseId, Long companyId, boolean isResend) {
        if (ObjectUtils.isEmpty(courseId) || ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(isResend)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        CoursePubMsg coursePubMsg = null;

        // 若要重新发送消息，则不需要在创建CoursePub & CoursePubMsg 数据
        if (isResend) {
            // 查询课程发布信息
            LambdaQueryWrapper<CoursePubMsg> pubMsgQueryWrapper = new LambdaQueryWrapper<>();
            pubMsgQueryWrapper.eq(CoursePubMsg::getCourseId, courseId);
            pubMsgQueryWrapper.eq(CoursePubMsg::getCompanyId, companyId);
            CoursePubMsg result = coursePubMsgService.getOne(pubMsgQueryWrapper);
            // 定时器重新发送消息时，消息表数据不存在
            if (ObjectUtils.isEmpty(result)) {
                log.error("消息数据不存在，courseBaseId => {}", courseId);
                return;
            }
        } else {
            // 查询课程基础信息
            // CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseId, companyId);
            // String auditStatus = courseBase.getAuditStatus();
            // 审核状态必须是 - 审核通过
            // boolean isCreateStatus = CourseAuditStatusPredicate.predicate(auditStatus, code -> CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus));
            // ExceptionCast.cast(!isCreateStatus, ContentErrorCode.E_120015);

            // 获取课程发布信息
            CoursePub coursePub = this.generateCoursePub(courseId, companyId, true);
            // 组装CoursePubMsg数据，CoursePubMsg的主键为CoursePub的ID
            coursePubMsg = CoursePubMsg.builder().pubId(coursePub.getId()).pubName(coursePub.getName()).pubStatus(CoursePubMsg.UNSENT).courseId(courseId).companyId(companyId).build();
            boolean pubMsgSaveResult = coursePubMsgService.save(coursePubMsg);
            ExceptionCast.cast(!pubMsgSaveResult, ContentErrorCode.E_120205);
        }

        // 向MQ投递消息，指定ID属性，确保消息的唯一性
        CorrelationData correlationData = new CorrelationData(String.valueOf(coursePubMsg.getPubId()));
        correlationData.getFuture().addCallback(
                // 交换机响应成功
                confirm -> {
                    // 交换机是否接收消息成功
                    boolean ack = confirm.isAck();
                    if (ack) {
                        // 交换机成功接受消息后，要修改本地的数据状态
                        executeChangeLocalStatusData(correlationData.getId());
                    } else {
                        log.error("发布消息数据失败，coursePubId => ", correlationData.getId());
                    }
                },
                // 交换机响应失败
                throwable -> {
                    log.error("发送课程发布的消息失败 => {}，失败标识数据: coursePubId => {}, errorMsg => {}", throwable.getCause(), correlationData.getId(), throwable.getMessage());
                }
        );

        // 将课程发布数据转为json
        String coursePubMsgJson = JsonUtil.objectTojson(coursePubMsg);
        // 发送消息
        rabbitTemplate.convertAndSend(exchange, routingKey, coursePubMsgJson, correlationData);


    }

    /**
     * 修改CoursePubMsg数据状态（用于MQ Exchange 接受消息成功）
     * 该方法必须为public，私有方法会导致事务失效
     *
     * @param coursePubId
     */
    @Transactional
    public void executeChangeLocalStatusData(String coursePubId) {
        // 判断消息是否被修改
        LambdaQueryWrapper<CoursePubMsg> pubMsgQueryWrapper = new LambdaQueryWrapper<>();
        pubMsgQueryWrapper.eq(CoursePubMsg::getPubId, Long.valueOf(coursePubId));
        pubMsgQueryWrapper.eq(CoursePubMsg::getPubStatus, CoursePubMsg.UNSENT);
        CoursePubMsg coursePubMsg = coursePubMsgService.getOne(pubMsgQueryWrapper);
        if (ObjectUtils.isEmpty(coursePubMsg)) {
            log.info("消息数据已经被修改过了, coursePubId => {}", coursePubMsg.getPubId());
            return;
        }

        // 修改课程基础信息表，审核状态为已发布
        LambdaUpdateWrapper<CourseBase> baseUpdateWrapper = new LambdaUpdateWrapper<>();
        baseUpdateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        baseUpdateWrapper.set(CourseBase::getCoursePubId, coursePubId);
        baseUpdateWrapper.eq(CourseBase::getId, coursePubMsg.getCourseId());
        boolean baseUpdateResult = courseBaseService.update(baseUpdateWrapper);
        if (!baseUpdateResult) {
            log.error("修改课程基础信息发布状态失败, coursePubId => {}", coursePubId);
            return;
        }

        // 修改课程发布消息表，发布状态为已发布
        LambdaUpdateWrapper<CoursePubMsg> pubMsgUpdateWrapper = new LambdaUpdateWrapper<>();
        pubMsgUpdateWrapper.set(CoursePubMsg::getPubStatus, CoursePubMsg.SENT);
        pubMsgUpdateWrapper.eq(CoursePubMsg::getPubId, coursePubId);
        boolean pubMsgUpdateResult = coursePubMsgService.update(pubMsgUpdateWrapper);
        if (!pubMsgUpdateResult) {
            log.error("修改课程发布消息状态失败, coursePubId => {}", coursePubId);
            ExceptionCast.cast(ContentErrorCode.E_120205);
        }
    }
}
